#define ENT_PER_CLUS 8
#define MAX_OPEN_FILES 20
#define EoC 0x0FFFFFF8
#define MAX_FILENAME_LENGTH 64
#define MAX_INPUT_LENGTH 256

struct ret {
	bool status;
	char message[50];
};

void fsInfo();
struct ret open(char name[], char mode[]);
struct ret close(char name[]);
bool create(char name[]);
struct ret read(char name[], uint startPos, uint numBytes);
struct ret write(char name[], uint startPos, char quotedData[]);
bool rm(char name[]);
struct ret srm(char name[]);
bool cd(char name[]);
bool mkdir(char name[]);
bool rmdir(char name[]);
uint size(char name[]);
void ls();

void readBoot();
void readFSInfo();
void readRoot();
void calculations();
struct secOfEnts readCluster(uint clusterNum);
uint32_t clusterOffsetInBytes(uint clusterNum);
uint32_t thisFatEntOffset(uint clusterNum, uint fatNum);
uint32_t readFatEntry(uint clusterNum);
void writeFatEntry(uint clusterNum, uint32_t newValue, uint fatNum);
uint32_t findLastFileFatEntry(uint clusterNum);
bool allocateNewCluster(uint clusterNum);
void addDotFiles(uint clusterNum, uint parentClusterNum);
uint16_t convertEndian16(uint16_t num);
uint32_t convertEndian32(uint32_t num);
uint64_t convertEndian64(uint64_t num);
uint32_t findFreeCluster();
void updateSectorDirEnts(uint clusterNum, struct secOfEnts ents);
void updateFSInfo();

uint32_t rootDirSectors, firstDataSector, FATOffsetInBytes;
FILE* file;
//struct oftEntry ofTable[10];
char curDir[12];
uint curClust;

struct bootSec {
	uint16_t BPB_BytesPerSec;
	uint8_t BPB_SecPerClus;
	uint16_t BPB_RsvdSecCnt;
	uint8_t BPB_NumFATs;
	uint16_t BPB_RootEntCnt;
	uint16_t BPB_TotSec16;
	uint8_t BPB_Media;
	uint16_t BPB_FATSz16;
	uint16_t BPB_SecPerTrk;
	uint16_t BPB_NumHeads;
	uint32_t BPB_HiddSec;
	uint32_t BPB_TotSec32;
	uint32_t BPB_FATSz32;
	uint16_t BPB_ExtFlags;
	uint16_t BPB_FSVer;
	uint32_t BPB_RootClus;
	uint16_t BPB_FSInfo;
	uint16_t BPB_BackupBootSector;
}__attribute__ ((__packed__)) bootSec;

struct FSInfo {
	uint32_t FSI_LeadSig;
	char FSI_Reserved1[480];
	uint32_t FSI_StructSig;
	uint32_t FSI_Free_Count;
	uint32_t FSI_Nxt_Free;
	char FSI_Reserved2[12];
	uint32_t FSI_TrailSig;
}__attribute__ ((__packed__)) FSInfo;

struct dirEnt {
	char LONG_NAME[32];
	char DIR_Name[11];
	uint8_t DIR_Attr;
	uint8_t DIR_NTRes;
	uint8_t DIR_CrtTimeTenth;
	uint16_t DIR_CrtTime;
	uint16_t DIR_CrtDate;
	uint16_t DIR_LstAccDate;
	uint16_t DIR_FstClustHI;
	uint16_t DIR_WrtTime;
	uint16_t DIR_WrtDate;
	uint16_t DIR_FstClusLO;
	uint32_t DIR_FileSize;
}__attribute__ ((__packed__));

struct secOfEnts {
	struct dirEnt entries[ENT_PER_CLUS];
}__attribute__ ((__packed__)) rootEnts;

// open file table entry
struct oftEntry {
	char name[12];
	char mode; // 0 = read, 1 = write, 2 = rw/wr
	uint cluster;
	uint size;
	uint dirEntCluster;
} ofTable[MAX_OPEN_FILES];

static inline bool is_host_little_endian() {
#ifdef _BSD_SOURCE
#include <endian.h>
	return (__BYTE_ORDER == __LITTLE_ENDIAN);
#else
	println_err("endian.h is not available on your system.");
	exit(EXIT_FAILURE);
#endif
}

static inline unsigned short bswap_16(unsigned short x) {
	return (x >> 8) | (x << 8);
}

static inline unsigned int bswap_32(unsigned int x) {
	return (bswap_16(x & 0xffff) << 16) | (bswap_16(x >> 16));
}

static inline unsigned long long bswap_64(unsigned long long x) {
	return (((unsigned long long) bswap_32(x & 0xffffffffull)) << 32)
			| (bswap_32(x >> 32));
}

#define htobe16(x) bswap_16(x)
#define htobe32(x) bswap_32(x)
#define htobe64(x) bswap_64(x)
#define htole16(x) x
#define htole32(x) x
#define htole64(x) x

